﻿using System;
using System.ComponentModel;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Markup;
using System.Collections;

namespace DefensiveShield
{
    //=================================================================================================================================
    //==================================================================================================== OS =========================
    //=================================================================================================================================
    public static partial class OS
    {
        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- OS INITIALIZATION ----------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Данная функция инициализирует основной класс OS
             * 
             * Её необходимо выполнять в начале каждого запуска продукта.
             * 
            @return Код ошибки, при её наличии
        */
        public static ErrorCodes InitializeOS()
        {
            try
            {
                bool createdNew;
                _mutexForSingleApplicationInstance = new Mutex(true, "ApplicationMutex", out createdNew);
                if (!createdNew)
                    return ErrorCodes.ErrorStartingSeveralApplicationInstances;

                var error = CheckOSEnvironment();
                if (error != ErrorCodes.NoError)
                    return error;

                Users = new Dictionary<string, UserPassport>();
                Files = new Dictionary<string, FilePassport>();
                FileIntegrity = new Dictionary<string, IntegrityState>();
                DateLastIntegritySnapshot = new DateTime(2014, 11, 11, 12, 0, 0);
                Sessions = new List<OsSession>();

                // Creating baseDirectory in ApplicationData folder for user
                // string baseDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Defensive Shield";
                // Directory.CreateDirectory(baseDirectory);

                var baseDirectory = Directory.GetCurrentDirectory();

                AdministratorName = null;
                _baseSystemConfigurationFileName = baseDirectory + "\\DefensiveShieldCryptoSettings.txt";
                _configurationFileName = baseDirectory + "\\DefensiveShieldSettings.xml";
                _logFullFileName = baseDirectory + "\\DefensiveShield.log";
                GlobalKey = GenerateKey(); // This is global key kiddie!!!
                IV = GenerateHashIV();
                
                //var retVal = ReadConfigurationFromFile(_configurationFileName);
                //if (retVal != ErrorCodes.NoError)
                    //LogMessage("", "Error reading configuration file. System will start as it is its installation.", _configurationFileName);

                //LogMessage("", "Successful system initialization");
                return ErrorCodes.NoError;
            }
            catch (Exception except)
            {
                //LogMessage("", "Error in Initializing OS. Exception: " + except.Message);
                MessageBox.Show("Error in Initializing OS. Exception: " + except.Message);
                return ErrorCodes.UnknownError;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- OS INITIALIZATION ----------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Данная функция проверяет необходимые требования системы, без которых дальнейная работа невозможна
             * 
             * Эту функцию необходимо выполнять в начале каждого запуска продукта.
             * 
            @return Код ошибки, при её наличии
        */

        public static ErrorCodes CheckOSEnvironment()
        {
            try
            {
                var retVal = CheckCryptoDllConnection();
                if (retVal != ErrorCodes.NoError)
                    return retVal;
                return ErrorCodes.NoError;
            }
            catch (Exception exception)
            {
                MessageBox.Show("Ошибка окружения файла, некоторые необходимые файлы могут отсутствовать, или быть повреждены.");
                return ErrorCodes.UnknownError;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- OS INITIALIZATION ----------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Данная функция проверяет должна ли система быть проинсталлирвоана, или запущена
             * 
             * Эту функцию необходимо выполнять для определения дальнейшего сценария поведения.
             * 
            @return bool - Истина, если система должна быть проинсталлирванна, ложь - иначе
        */

        public static bool CheckOSInstallation()
        {
            return File.Exists(_baseSystemConfigurationFileName) == false ||
                   File.Exists(_configurationFileName) == false;
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- CREATE SESSION -------------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Данная функция предназначена для разблокировки системы
             * 
             * Данная функция предназначена для того, чтобы расшифровать глобальный ключ, для успешной дальнейшей работы системы
             * 
            @return ErrorCodes - возвращает код ошибки
        */

        public static ErrorCodes UnlockSystem(string systemPassword)
        {
            try
            {
                var systemKey = ConstructKeyFromPassword(systemPassword);
                var file = new StreamReader(_baseSystemConfigurationFileName);

                var byteConverter = new ByteConverter();
                var decryptedGlobalKey = Decrypt(file.ReadLine(), systemKey);
                var decryptedIV = Decrypt(file.ReadLine(), systemKey);
                var decryptedIV2CheckGlobalKey = Decrypt(file.ReadLine(), decryptedGlobalKey);
                if (decryptedIV != decryptedIV2CheckGlobalKey)
                    return ErrorCodes.WrongGlobalKeyPassword;
                file.Close();

                SystemPassword = systemPassword;
                GlobalKey = decryptedGlobalKey;
                IV = decryptedIV;

                return ErrorCodes.NoError;
            }
            catch (Exception exception)
            {
                MessageBox.Show("Неизвестная ошибка при попытке дешифровать глобаьный ключ системы.");
                return ErrorCodes.UnknownError;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- CREATE SESSION -------------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Данная функция предназначена для сохранения настроек криптографической системы
             * 
             * Данная функция сохраняет глобальный лкюч шифрования, зашифрованный на пароле администратора, инициализационный вектор функции хеширования, зашифрованный на пароле администратора, и ещё один экземпляр зашифрованный на глобальном ключе системы, для проверки корректности дешифровки
             * 
            @return ErrorCodes - возвращает код ошибки
        */

        public static ErrorCodes SaveSystemCryptoInfo()
        {
            try
            {
                var systemKey = ConstructKeyFromPassword(SystemPassword);
                var encryptedGlobalKey = Encrypt(GlobalKey, systemKey);
                var encryptedIV = Encrypt(IV, systemKey);
                var encryptedIV2CheckGlobalKey = Encrypt(IV, GlobalKey);
                
                File.Delete(_baseSystemConfigurationFileName);
                File.AppendAllText(_baseSystemConfigurationFileName, encryptedGlobalKey + Environment.NewLine);
                File.AppendAllText(_baseSystemConfigurationFileName, encryptedIV + Environment.NewLine);
                File.AppendAllText(_baseSystemConfigurationFileName, encryptedIV2CheckGlobalKey + Environment.NewLine);

                return ErrorCodes.NoError;
            }
            catch (Exception exception)
            {
                MessageBox.Show("Неизвестная ошибка при попытке сохранить криптографические параметры системы. Дальнейшее поведение системы не предсказуемо.");
                return ErrorCodes.UnknownError;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- CREATE SESSION -------------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Данная функция создаёт новую сессию и возвращает её
             * 
             * Сессия сохраняется в список сессий, откуда с ними потом можно оперировать
             * 
            @return ссылка на созданный OsSession
            @return null - в случае ошибки
        */
        public static OsSession CreateOsSession()
        {
            try
            {
                var osSession = new OsSession();
                Sessions.Add(osSession);

                LogMessage("", "Successful Os Session creation.");
                return osSession;
            }
            catch (Exception except)
            {
                LogMessage("", "Error in creating Os Session. Exception: " + except.Message);
                return null;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- DELETE OS SESSION ----------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Данная функция удаляет указанную пользовательскую сессию
            @return Код ошибки, при её наличии
        */
        public static ErrorCodes DeleteOsSession(OsSession osSession)
        {
            try
            {
                osSession.LogOut();
                if (!Sessions.Remove(osSession))
                {
                    LogMessage(osSession.UserName, "Error closing os session, no such session.");
                    return ErrorCodes.ErrorInDeletingOsSession;
                }

                LogMessage(osSession.UserName, "Successful session closing.");
                return ErrorCodes.NoError;
            }
            catch (Exception except)
            {
                LogMessage(osSession.UserName, "Error closing Os Session. Exception: " + except.Message);
                return ErrorCodes.UnknownError;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- SAVING OS SETTINGS ---------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief функция, сохраняющая всю служебную информацию в системе в некоторый шифрованный файл
            @return Код ошибки, при её наличии
        */
        public static ErrorCodes ShutdownOS()
        {
            if (AdministratorName != null)
            {
                var retVal = SaveSystemCryptoInfo();
                if (retVal != ErrorCodes.NoError)
                {
                    LogMessage("", "Error saving cryptografic configurations. Скорее всего это хана.",
                        _baseSystemConfigurationFileName);
                    MessageBox.Show(
                        "Ошибка сохранения криптографических настроек системы на основе данных администратора. Фатальная ошибка системы. Данные останутся зашифрованными.");
                    return retVal;
                }
                retVal = WriteConfigurationToFile();
                if (retVal != ErrorCodes.NoError)
                {
                    LogMessage("", "Error saving configurations.", _configurationFileName);
                    MessageBox.Show(
                        "Ошибка сохранения настроек системы на основе данных администратора. Фатальная ошибка системы. Данные останутся зашифрованными.");
                    return retVal;
                }

                LogMessage("", "System shoot down.");
            }

            Users.Clear();
            Files.Clear();
            Sessions.Clear();
            AdministratorName = "";
            FileIntegrity.Clear();
            GlobalKey = "";
            IV = "";
            _baseSystemConfigurationFileName = "";
            _configurationFileName = "";
            _logFullFileName = "";

            return ErrorCodes.NoError;
        }
    }
}
